/*

    tools23d - tools for converting images to 3D formats.
    Copyright (C) 2014  Dave Gorst

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

/**
 * @file   image.c
 * @brief  Main image processing functions.
 *
 * This file contains non-backend specific image processing functions.
 */

#include <stdio.h>
#include <time.h>

#include <FreeImage.h>
#include "tools23d.h"

#ifdef BUILD_BTIME
	#include "btime23d.h"
#else
	#include "photo23d.h"
#endif

#include "frameset.h"
#include "image-fi.h"
#include "image.h"
#include "image-calc.h"
#include "util.h"

static unsigned int new_image;

/**
Initial setup of image backend.

@param	target_details	Details of output image.
@param	filter		Filter to use when scaling.
@param	log_level		Log level.
@return ::TRUE if successful.
*/

BOOL T3D_InitialiseImageHandling (image_details * target_details, Filters filter, LogLevel log_level)
{
	BOOL success;

	T3D_InitialiseBackend();

	/* Set scaling filter. */

	T3D_SetActiveFilter (filter);

	success = T3D_CreateImage
	(
		&new_image,
		target_details->width,
		target_details->height,
		target_details->depth
	);

	return success;
}


/**
Shutdown image backend.

*/

void T3D_ShutdownImageHandling (void)
{
	T3D_DestroyImage (new_image);

	T3D_ShutdownBackend();
}

/**
Helper function to call picture load routines in specific image backends.

@param	left_filename	Filename of left image as string
@param	right_filename	Filename of right image as string
@param	left_picnumber	Pointer used to return new index for loaded left image
@param	right_picnumber	Pointer used to return new index for loaded right image
@return Boolean used to indicate whether loadings were successful. ::TRUE if OK.
*/

BOOL T3D_DoLoad (char * left_filename, char * right_filename, unsigned int * left_picnumber, unsigned int * right_picnumber)
{
	if (T3D_LoadImage (left_picnumber, left_filename))
	{
		if (T3D_LoadImage (right_picnumber, right_filename))
			return TRUE;
		else
			T3D_SetLastError ("Unable to load right image %s.\n", right_filename);
	}
	else
		T3D_SetLastError ("Unable to load left image %s.\n", left_filename);

	return FALSE;
}

/**
Helper function to call picture destruction routines in specific image backends.

@param	left_picnumber		Index of left image
@param	right_picnumber	Index of right image
*/

void T3D_DoClear (unsigned int left_picnumber, unsigned int right_picnumber)
{
	T3D_DestroyImage(left_picnumber);
	T3D_DestroyImage(right_picnumber);
}

/**
Retrieve image details and check they are uniform for both left and right images.

@param	left_filename	Filename of left image
@param	right_filename	Filename of right image
@param	width	Pointer to width value
@param	height	Pointer to height value
@param	depth	Pointer to bit depth value
@return Boolean value indicating whether details retrieval was successful. ::TRUE if OK.
*/

BOOL T3D_DoCheck (char * left_filename, char * right_filename, unsigned int * width, unsigned int * height, unsigned int * depth)
{
	unsigned int read_width, read_height, read_depth;
	IMAGE_FORMAT filetype;

	/* TODO - do we need to check filetypes are the same? Does it matter? */

	if (T3D_RetrievePictureDetails (left_filename, &filetype, &read_width, &read_height, &read_depth))
	{
		if (T3D_RetrievePictureDetails (right_filename, &filetype, width, height, depth))
		{
			if (!(read_width == *width) && (read_height == *height) && (read_depth== *depth))
			{
				T3D_SetLastError("Left and right image details do not match.");
				return FALSE;
			}
			else
				return TRUE;
		}
		else
			return FALSE;
	}
	else
		return FALSE;
}

BOOL T3D_DoColourConversion (image_index * left_image, image_index * right_image, ColourScheme colourscheme)
{
	return TRUE;
}

/**
Perform the zooming captain.

@param  details Structure containing the size and bit depth of the source image.
@param  left_image  Pointer to the index of the left image.
@param  right_image Pointer to the index of the right image.
@param  zoom    Value indicating how much to zoom in or out by, as a percentage of the size of the original image.
@param	border_width	Pointer to integer which will contain the amount of horizontal pixels by which the new image is now larger (if positive) or smaller than (if negative) the original width.
@param	border_height	Pointer to integer which will contain the amount of vertical pixels by which the new image is now larger (if positive) or smaller than (if negative) the original width.
@return ::TRUE if successful, ::FALSE otherwise.
*/

BOOL T3D_DoZoom (image_details * details, image_index * left_image, image_index * right_image, unsigned int zoom, int * border_width, int * border_height)
{
    unsigned int width = details->width;
    unsigned int height = details->height;

    if (zoom != 100)
    {
        T3D_CalculateZoomBorders (zoom, &width, &height, border_width, border_height);

		if (T3D_ScaleImage(left_image, width, height))
			if (T3D_ScaleImage(right_image, width, height))
				return TRUE;
			else
				return FALSE;
		else
			return FALSE;
    }
	else
		return TRUE;

}

/**
Perform the cropping.

@param  details Structure containing the size and bit depth of the source image.
@param  left_image  Pointer to the index of the left image.
@param  right_image Pointer to the index of the right image.
@param	border_width	Pointer to integer which will contain the amount of horizontal pixels by which the new image is now larger (if positive) or smaller than (if negative) the original width.
@param	border_height	Pointer to integer which will contain the amount of vertical pixels by which the new image is now larger (if positive) or smaller than (if negative) the original width.
@param  croptype    Enum indicating whether we want to crop full-frame to widescreen and if so whether to do so from the top, middle or bottom.
@return ::TRUE if successful, ::FALSE otherwise.
*/

BOOL T3D_DoCrop (image_details * details, image_index * left_image, image_index * right_image,int border_width, int border_height, AutoCropTypes croptype)
{
    unsigned int top = 0;
	unsigned int crop_height = details->height;

    if (croptype != None)
    {
		crop_height = T3D_CalculateCropToWS (details->width + (border_width * 2), details->height + (border_height * 2));

		switch (croptype)
		{
		case Top:
			/* TODO */
			top = border_height;
			break;
		case Bottom:
			top = details->height - crop_height - border_height;
			break;
		case Centre:
		default:
			top = (details->height / 2) + border_height - (crop_height / 2);
			break;
		}

	}

	if (T3D_CropImage (left_image, 0, top, details->width, crop_height))
        if (T3D_CropImage (right_image, 0, top, details->width, crop_height))
            return TRUE;
        else
		{
			T3D_SetLastError ("Failed to crop right image to top %d left 0 width %d height %d.", top, details->width, crop_height);
            return FALSE;
		}
    else
	{
		T3D_SetLastError ("Failed to crop left image to top %d left 0 width %d height %d.", top, details->width, crop_height);
        return FALSE;
	}
}

BOOL T3D_Write3DImage (char * left_filename, char * right_filename, char * write_filename, image_details * target_details, Mode picture_mode, Filters filter, AutoCropTypes croptype, BOOL preserve_ar, LogLevel log_level, unsigned int zoom)
{
	BOOL success;
	image_index left_image;
	image_index right_image;
	unsigned int left_x, left_y, right_x, right_y;
	int border_width, border_height;

	image_details details;

	unsigned int output_width, output_height;

	if (log_level >= Verbose)
		(void) fprintf (stdout, "Received output filename \"%s\".\n", write_filename);

	if (!T3D_DoCheck (left_filename, right_filename, &(details.width), &(details.height), &(details.depth)))
		return FALSE;

	T3D_CalculateOutputSize (picture_mode, preserve_ar, details.width, details.height, target_details->width, target_details->height, &output_width, &output_height);

	if (log_level > Standard)
		T3D_OutputMessage ("Reading left image \"%s\".\nReading right image \"%s\".\n", left_filename, right_filename);

	if (T3D_DoLoad (left_filename, right_filename, &left_image, &right_image))
	{

        T3D_DoZoom(&details, &left_image, &right_image, zoom, &border_width, &border_height);
        T3D_DoCrop(&details, &left_image, &right_image, border_width, border_height, croptype);



		if (log_level > Standard)
			T3D_OutputMessage ("Scaling output images to %d by %d.\n", output_width, output_height);

		T3D_ScaleImage (&left_image, output_width, output_height);
		T3D_ScaleImage (&right_image, output_width, output_height);

		T3D_CalculateWritePosition(picture_mode, target_details, output_width, output_height, &left_x, &left_y, &right_x, &right_y);

		T3D_CopyInto(left_image, new_image, left_x, left_y);
		T3D_CopyInto(right_image, new_image, right_x, right_y);

		if (log_level >= Standard)
			T3D_OutputMessage ("Saving image \"%s\".\n", write_filename);

		success = T3D_WriteImageToFile (new_image, write_filename);

	}
	else
		success = FALSE;

	T3D_DoClear (left_image, right_image);

	return success;
}
